﻿#region Copyright (c) Lokad 2010
// This code is released under the terms of the new BSD licence.
// URL: http://www.lokad.com/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using Lokad.Monitoring.Persistence.Repositories;
using Lokad.Monitoring.Rsm;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace Lokad.Monitoring.Worker
{
    public class WorkerRole : RoleEntryPoint
    {
        // Helper provided to run the role as a stand-alone console.
        public static void Main(string[] args)
        {
            var role = new WorkerRole();
            role.Run();
        }

        public override void Run()
        {
            var endpoints = GlobalSetup.Container.Resolve<EndpointRepository>();
            var responses = GlobalSetup.Container.Resolve<ResponseRepository>();
            var reports = GlobalSetup.Container.Resolve<ReportRepository>();

            // loop just grap )
            while (true)
            {
                var updatedEndpoints = new List<string>();

                // Refreshing the endpoints
                foreach(var endpoint in endpoints.List())
                {
                    if(endpoint.Frequency <= 0 || // zero frequency --> disabled endpoint
                        DateTime.UtcNow.Subtract(endpoint.LastRetrieved).TotalSeconds < endpoint.Frequency)
                    {
                        continue;
                    }

                    updatedEndpoints.Add(endpoint.Id);

                    try
                    {
                        var client = new RsmClient();
                        var doc = client.Open(endpoint.Url, endpoint.BasicAuth);
                        responses.Create(endpoint, doc);
                    }
                    catch (Exception ex)
                    {
                        // message to indicate the fault (this one will persist)
                        var message = new Message
                                          {
                                              Title = "Endpoint unreachable",
                                              Summary = ex.ToString(),
                                              Updated = DateTime.UtcNow,
                                              Tags = "fault"
                                          };

                        // faulty indicator to flag whole report as faulty
                        var indicator = new Indicator
                                            {
                                                Name = "/system/endpoint",
                                                Updated = DateTime.UtcNow,
                                                Tags = "fault endpoint",
                                                Value = "unreachable"
                                            };

                        var doc = new RsmDocument();
                        doc.Messages.Add(message);
                        doc.Indicators.Add(indicator);

                        responses.Create(endpoint, doc);
                    }

                    // success or failure, the endpoint is mark as retrieved
                    endpoints.UpdateLastRetrieved(endpoint.Id);
                }

                // Refreshing the reports
                foreach(var report in reports.List())
                {
                    // only the endpoint is matching
                    if(Split(report.Endpoints).Intersect(updatedEndpoints).Any())
                    {
                        reports.Generate(report.Id); 
                    }
                }

                Thread.Sleep(10 * 1000); // 10s sleep (just a magic constant for now)
            }
        }

        static string[] Split(string str)
        {
            return string.IsNullOrEmpty(str) ?
                new string[0] :
                str.Split().Where(s => !string.IsNullOrEmpty(s)).ToArray();
        }

        public override bool OnStart()
        {
            // Set the maximum number of concurrent connections 
            ServicePointManager.DefaultConnectionLimit = 12;

            // For information on handling configuration changes
            // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
            RoleEnvironment.Changing += RoleEnvironmentChanging;

            return base.OnStart();
        }

        private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
        {
            // If a configuration setting is changing
            if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
            {
                // Set e.Cancel to true to restart this role instance
                e.Cancel = true;
            }
        }
    }
}
